package huobi

import (
	"global/libs/structs"
	"srv/kline_1min/exchanges"
	"fmt"
	"strings"
	"global/model"
	exchanges2 "global/libs/exchanges"
	"encoding/json"
	"github.com/ngaut/log"
	"time"
	"strconv"
	"global/core"
	"srv/kline/exchange"
	"github.com/shopspring/decimal"
)

type HuobiKlineHandle struct {
	base
	exchange 			string
	table 				string
	symbol 				*structs.Symbol
	symbolStr 			string

	// 去重复数据
	repeats 			map[string]struct{}

	max 				int
	last 				model.Kline 		// 最后一个

	tickerC 			*time.Ticker
}

func NewHuobiKlineHandle(exchange string, symbol *structs.Symbol) exchanges.KlineInterface {
	return &HuobiKlineHandle{
		exchange: 		exchange,
		table: 			fmt.Sprintf("kline_%s", strings.ToLower(symbol.From)),
		symbol: 		symbol,
		symbolStr: 		fmt.Sprintf("%s_%s", strings.ToLower(symbol.From), strings.ToLower(symbol.To)),
		repeats: 		make(map[string]struct{}),
		tickerC: 		time.NewTicker(1 * time.Minute),
	}
}

// 是否需要获取历史数据
func (h *HuobiKlineHandle) NeedGetHistoryData() (bool, error) {
	has, err := model.KlineModel.HasItem(h.table, h.exchange, h.symbol)
	if err != nil {
		return false, err
	}
	return !has, nil
}

func (h *HuobiKlineHandle) History() {
	var (
		to string
		err error
		data []*model.Kline
	)
	// 获取最小时间，历史最高价，历史最低价

	for {
		data, to, err = h.request(to)
		if err != nil {
			log.Errorf("Error: %s", err)
			continue
		}

		// 去重复
		var storeDatas []*model.Kline
		for _, v := range data {
			if _, ok := h.repeats[v.Time]; !ok {
				storeDatas = append(storeDatas, v)
				h.repeats[v.Time] = struct{}{}
			}
		}

		if len(storeDatas) == 0 {
			break
		}

		// save
		h.save(storeDatas)
	}

	h.repeats = nil
}

func (h *HuobiKlineHandle) Latest() {
	max, err := model.KlineModel.GetMax(h.table, h.exchange, h.symbol)
	if err != nil {
		log.Errorf("get max time failed, err: %s", err)
		return
	}
	log.Infof("get max item, exchange:%s, symbol:%s, max:%d",
		h.exchange, h.symbolStr, max)
	if max == 0 {
		return
	}

	h.max = max

	for t := range h.tickerC.C {
		_ = t
		datas, _, err := h.request("")
		if err != nil {
			log.Errorf("request error: %s", err)
			continue
		}

		var latestMax int
		var storeDatas []*model.Kline
		for _, v := range datas {

			newTime, _ := strconv.Atoi(v.Time)
			if latestMax < newTime {
				latestMax = newTime
			}

			if newTime == h.max {
				// 修改
				log.Infof("update item: exchange:%s, symbol: %s, time:%s",
					h.exchange, h.symbolStr, v.Time)
				model.KlineModel.Update(h.table, v)
			} else if newTime > h.max {
				// 插入
				storeDatas = append(storeDatas, v)
			}
		}

		log.Infof("insert items, exchange: %s, symbol:%s, length: %d",
			h.exchange, h.symbolStr, len(storeDatas))
		if len(storeDatas) > 0 {
			h.save(storeDatas)
		}

		h.max = latestMax
	}
}

func (h *HuobiKlineHandle) save(datas []*model.Kline)  {
	_, err := model.KlineModel.InsertMulti(h.table, datas)
	if err != nil {
		// 如果出错就逐条插入
		for _, v := range datas {
			if _, err := model.KlineModel.Insert(h.table, v); err != nil {
				log.Errorf("Error: %s", err)
			}
		}
	}

}

// 请求数据
func (h *HuobiKlineHandle) request(to string) ([]*model.Kline, string, error) {
	url := core.GetHuobiMarketUrl("/history/kline")
	params := map[string]interface{}{
		"size": 		2000,
		"symbol": 		strings.ToLower(fmt.Sprintf("%s%s", h.symbol.From, h.symbol.To)),
		"period": 		exchange.HuobiKlineResolution1min,
	}

	datas, err := h.get(url, params)
	if err != nil {
		return nil, to, err
	}

	type responseStruct struct {
		Data		[]*exchanges2.HuobiKline 		`json:"data"`
	}

	var response responseStruct
	if err := json.Unmarshal([]byte(datas), &response); err != nil {
		return nil, to, err
	}

	if len(response.Data) == 0 {
		return nil, to, nil
	}

	results := make([]*model.Kline, 0)
	for _, v := range response.Data {
		results = append(results, &model.Kline{
			Exchange: 		"huobi",
			Symbol: 		h.symbolStr,
			CoinBase: 		h.symbol.From,
			CoinQuote: 		h.symbol.To,
			Time: 			strconv.Itoa(v.Id * 1000),
			Open: 			decimal.NewFromFloat(v.Open).String(),
			High: 			decimal.NewFromFloat(v.High).String(),
			Low: 			decimal.NewFromFloat(v.Low).String(),
			Close: 			decimal.NewFromFloat(v.Close).String(),
			Volume: 		decimal.NewFromFloat(v.Vol).String(),
			Datetime: 		time.Unix(int64(v.Id), 0).Format("2006-01-02 15:04:05"),
		})
	}

	return results, to, nil
}